package cn.lyf.currey;

import static cn.lyf.currey.Currying.*;
import java.util.*;
import java.util.function.*;

import cn.lyf.currey.Currying.f1;

// 主要提供偏函数的支持
/*
偏函数是指那些只能处理问题域里一部分问题的函数
这里用返回 Optional 的函数模拟偏函数
返回Optional.empty() 函数表示无法处理该问题
*/
public class Partial {
    /*
     * 给函数结果进行包装 .一般用于把函数结果存入容器中 便于和其他函数组合
     */

    public static <E, C> f1<Optional<E>, Optional<C>> wrapped(Supplier<C> sup, BiConsumer<C, E> acc) {
        return e -> {
            if (e.isEmpty())
                return Optional.empty();
            C c = sup.get();
            acc.accept(c, e.get());
            return Optional.of(c);
        };
    }
// 参数为0的偏函数接口同时也实现了Lazy接口
    public static interface pf0<A> extends Lazy<Optional<A>> {
        default pf0<A> lift(Supplier<A> partFx) {
            return () -> {
                return Optional.ofNullable(this.get().orElse(partFx.get()));
            };
        }

        default <C> pf0<C> wrapped(Supplier<C> supplier, BiConsumer<C, A> accumulator) {
            return () -> {
                Optional<A> r = this.get();
                if (r.isEmpty())
                    return Optional.empty();
                C c = supplier.get();
                accumulator.accept(c, r.get());
                return Optional.of(c);
            };
        }
    }

    public static interface pf1<A, B> extends f1<A, Optional<B>> {
        default pf1<A, B> lift(Function<? super A, ? extends B> partFx) {
            return a -> {
                return Optional.ofNullable(this.apply(a).orElse(partFx.apply(a)));
            };
        }

        default <C> pf1<A, C> wrap(Function<Optional<B>, Optional<C>> f) {
            return a -> f.apply(apply(a));
        }
    }

    public static interface pf2<A, B, C> extends f2<A, B, Optional<C>> {
        default pf2<A, B, C> lift(BiFunction<? super A, ? super B, ? extends C> partFx) {
            return (a, b) -> {
                return Optional.ofNullable(this.apply(a, b).orElse(partFx.apply(a, b)));
            };
        }

        default <D> pf2<A, B, D> wrap(Function<Optional<C>, Optional<D>> f) {
            return (a, b) -> f.apply(apply(a, b));
        }
    }

    public static interface pf3<A, B, C, D> extends f3<A, B, C, Optional<D>> {
        default pf3<A, B, C, D> lift(f3<A, B, C, D> partFx) {
            return (a, b, c) -> {
                return Optional.ofNullable(this.apply(a, b, c).orElse(partFx.apply(a, b, c)));
            };
        }

        default <G> pf3<A, B, C, G> wrap(Function<Optional<D>, Optional<G>> f) {
            return (a, b, c) -> f.apply(apply(a, b, c));
        }
    }

    public static interface pf4<A, B, C, D, E> extends f4<A, B, C, D, Optional<E>> {
        default pf4<A, B, C, D, E> lift(f4<A, B, C, D, E> partFx) {
            return (a, b, c, d) -> {
                return Optional.ofNullable(this.apply(a, b, c, d).orElse(partFx.apply(a, b, c, d)));
            };
        }

        default <G> pf4<A, B, C, D, G> wrap(Function<Optional<E>, Optional<G>> f) {
            return (a, b, c, d) -> f.apply(apply(a, b, c, d));
        }
    }

    public static interface pf5<A, B, C, D, E, F> extends f5<A, B, C, D, E, Optional<F>> {
        default pf5<A, B, C, D, E, F> lift(f5<A, B, C, D, E, F> partFx) {
            return (a, b, c, d, e) -> {
                return Optional.ofNullable(this.apply(a, b, c, d, e).orElse(partFx.apply(a, b, c, d, e)));
            };
        }

        default <G> pf5<A, B, C, D, E, G> wrap(Function<Optional<F>, Optional<G>> f) {
            return (a, b, c, d, e) -> f.apply(apply(a, b, c, d, e));
        }
    }

    @SafeVarargs
    @SuppressWarnings({ "unchecked" })
    // 组合多个偏函数，提升函数作用域。
    final static <A> pf0<A> lift(pf0<? extends A>... partFx) {
        return () -> {
            for (int i = 0; i < partFx.length; i++) {
                pf0<A> fx = (pf0<A>) partFx[i];
                Optional<A> r = (Optional<A>) fx.get();
                if (r.isPresent())
                    return r;
            }
            return Optional.empty();
        };
    }

    @SafeVarargs
    @SuppressWarnings({ "unchecked" })
    final public static <A, B> pf1<A, B> lift(pf1<? super A, ? extends B>... partFx) {
        return a -> {
            for (int i = 0; i < partFx.length; i++) {
                pf1<A, B> fx = (pf1<A, B>) partFx[i];
                Optional<B> r = (Optional<B>) fx.apply(a);
                if (r.isPresent())
                    return r;
            }
            return Optional.empty();
        };
    }

    @SafeVarargs
    @SuppressWarnings({ "unchecked" })
    final static <A, B, C> pf2<A, B, C> lift(pf2<? super A, ? super B, ? extends C>... partFx) {
        return (a, b) -> {
            for (int i = 0; i < partFx.length; i++) {
                pf2<A, B, C> fx = (pf2<A, B, C>) partFx[i];
                Optional<C> r = (Optional<C>) fx.apply(a, b);
                if (r.isPresent())
                    return r;
            }
            return Optional.empty();
        };
    }

    @SafeVarargs
    @SuppressWarnings({ "unchecked" })
    final static <A, B, C, D> pf3<A, B, C, D> lift(pf3<? super A, ? super B, ? super C, ? extends D>... partFx) {
        return (a, b, c) -> {
            for (int i = 0; i < partFx.length; i++) {
                pf3<A, B, C, D> fx = (pf3<A, B, C, D>) partFx[i];
                Optional<D> r = (Optional<D>) fx.apply(a, b, c);
                if (r.isPresent())
                    return r;
            }
            return Optional.empty();
        };
    }

    @SafeVarargs
    @SuppressWarnings({ "unchecked" })
    final static <A, B, C, D, E> pf4<A, B, C, D, E> lift(
            pf4<? super A, ? super B, ? super C, ? super D, ? extends E>... partFx) {
        return (a, b, c, d) -> {
            for (int i = 0; i < partFx.length; i++) {
                pf4<A, B, C, D, E> fx = (pf4<A, B, C, D, E>) partFx[i];
                Optional<E> r = (Optional<E>) fx.apply(a, b, c, d);
                if (r.isPresent())
                    return r;
            }
            return Optional.empty();
        };
    }

    @SafeVarargs
    @SuppressWarnings({ "unchecked" })
    final static <A, B, C, D, E, F> pf5<A, B, C, D, E, F> lift(
            pf5<? super A, ? super B, ? super C, ? super D, ? super E, ? extends F>... partFx) {
        return (a, b, c, d, e) -> {
            for (int i = 0; i < partFx.length; i++) {
                pf5<A, B, C, D, E, F> fx = (pf5<A, B, C, D, E, F>) partFx[i];
                Optional<F> r = (Optional<F>) fx.apply(a, b, c, d, e);
                if (r.isPresent())
                    return r;
            }
            return Optional.empty();
        };
    }

    // ArrayList::new, List::add
    // 收集器。用于收集函数返回结果
    public static <E, C> f1<E, C> collector(Supplier<C> supplier, BiConsumer<C, E> accumulator) {
        return e -> {
            if (e == null)
                return null;
            C container = supplier.get();
            accumulator.accept(container, e);
            return container;
        };
    }

}
